/**
* JimBot - Java IM Bot
* Copyright (C) 2006-2009 JimBot project
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package ru.jimbot.protocol;

import ru.caffeineim.protocols.icq.core.OscarConnection;
import ru.caffeineim.protocols.icq.exceptions.ContactListOperationException;
import ru.caffeineim.protocols.icq.exceptions.ConvertStringException;
import ru.caffeineim.protocols.icq.integration.OscarInterface;
import ru.caffeineim.protocols.icq.integration.events.*;
import ru.caffeineim.protocols.icq.integration.listeners.MessagingListener;
import ru.caffeineim.protocols.icq.integration.listeners.MetaInfoListener;
import ru.caffeineim.protocols.icq.integration.listeners.OurStatusListener;
import ru.caffeineim.protocols.icq.integration.listeners.XStatusListener;
import ru.caffeineim.protocols.icq.metainfo.FullUserInfo;
import ru.caffeineim.protocols.icq.setting.enumerations.ClientModeEnum;
import ru.caffeineim.protocols.icq.setting.enumerations.PrivateStatus;
import ru.caffeineim.protocols.icq.setting.enumerations.StatusFlagEnum;
import ru.caffeineim.protocols.icq.setting.enumerations.StatusModeEnum;
import ru.caffeineim.protocols.icq.setting.enumerations.XStatusModeEnum;
import ru.caffeineim.protocols.icq.tool.StaticData;
import ru.jimbot.modules.AbstractProps;
import ru.jimbot.modules.MsgOutQueue;
import ru.jimbot.util.Log;
import ru.jimbot.util.MainProps;

/**
 * Реализация ICQ-протокола
 * @author Prolubnikov Dmitry
 * @author fraer72
 * @author alex_xpert
 */
public class ICQProtocol extends AbstractProtocol implements OurStatusListener,
MessagingListener,XStatusListener, MetaInfoListener {
    private MetaAckResults metas= null;
    private AbstractProps props;
    private int xStatusId;
    private String xStatusText;

    public ICQProtocol(AbstractProps props){
        this.props = props;
        server = MainProps.getServer();
        port = MainProps.getPort();
        mq = new MsgOutQueue(this, props.getIntProperty("bot.pauseOut"),
        props.getIntProperty("bot.pauseRestart"),
        props.getIntProperty("bot.msgOutLimit"));
    }

    /**
     * Возвращает параметры сервиса
     */
    public AbstractProps getProps(){
        return props;
    }

    /**
     * Возвращает размер очереди исходящих сообщений
     */
    public int getOuteqSize(){
        return mq.size();
    }

    /**
     * Подключение
     */
    @Override
    public void connect() {
        mq.start();
        con = new OscarConnection(server, port, screenName, password, props.getBooleanProperty("web.aware.on.off"), false);
        con.addOurStatusListener(this);
        con.addMessagingListener(this);
        con.addXStatusListener(this);
        con.addMetaInfoListener(this);
        con.connect();
    }

    /**
     * Переподключение
     */
    @Override
    public void reConnect(){
        if(this.isOnLine()) {
            disconnect();
        }
        try {
            Thread.sleep(5000);
        } catch(Exception ex) {
            ex.printStackTrace();
        }
        this.connect();
    }

    /**
     * Отключение
     */
    @Override
    public void disconnect() {
        if(con == null) return;
        try {
            mq.stop();
            con.close();
            con.removeOurStatusListener(this);
            con.removeMessagingListener(this);
            con.removeXStatusListener(this);
            con.removeMetaInfoListener(this);
            con = null;
        }catch (Exception ex){
        }
    }

    /**
     * Прием сообщения
     */
    @Override
    public void getMsg(String sendSN, String recivSN, String msg, boolean isOffline){
        protList.getMsg(sendSN, recivSN, msg, isOffline);
    }

    /**
     * Подключен?
     */
    @Override
    public boolean isOnLine() {
        if(con == null) return false;
        return connected;
    }

    /**
     * Отправка сообщения
     */
    @Override
    public void sendMsg(String sn, String msg){
        try{
            OscarInterface.sendBasicMessage(con, sn, msg);
        }catch (ConvertStringException e){
            Log.info("ERROR send message: " + msg);
        }
    }

    /**
     * Запрашивать авторизацию?
     */
    @Override
    public boolean isNoAuthUin(String uin){
        return props.getBooleanProperty("chat.isAuthRequest");
    }

    /*
     * Обработка входящего сообщения
     */
    @Override 
    public void onIncomingMessage(IncomingMessageEvent e) {
        try {
            String msg = e.getMessage().toString();
            if(MainProps.isIgnor(e.getSenderID())) {
                Log.flood2("IGNORE LIST: " + e.getMessageId() + "->" + screenName + ": " + e.getMessage().toString());
            return;
            }
            if(e.getSenderID().equals("1")) {
                Log.error("Ошибка совместимости клиента ICQ. Будет произведена попытка переподключения...");
                con.close();
                return;
            } 
            if(msg.equals("addgroup")) {
                Log.info("Добавляю группу chat...");
                this.addGroup("chat");
                return;
            } 
            if(msg.equals("delgroup")) {
                Log.info("Удаляю группу chat...");
                this.delGroup("chat");
                return;
            } 
            protList.getMsg(e.getSenderID(), screenName, e.getMessage().toString(), false);
        } catch (Exception ex) {
            Log.error("error "+ex.getMessage().toString());
        }
    }


    /**
     * Ошибка подключения
     */
    @Override
    public void onAuthorizationFailed(LoginErrorEvent e){
        Log.error("На uin`не " + screenName +  " авторизация с сервером ICQ не удалась. Причина: " +  e.getErrorMessage());
        connected = false;
    }

    /**
     * Смена статуса
     */
    public void onStatusChange(StatusEvent e){
        Log.debug("StatusEvent: " + e.getStatusMode());
    }

    /**
     * Ответ на запрос X-Статуса
     */
    @Override
    public void onXStatusRequest(XStatusRequestEvent e) {
        try {
            OscarInterface.sendXStatus(con, new XStatusModeEnum(Integer.parseInt(props.getStringProperty("icq.xstatus"))),
            props.getStringProperty("icq.STATUS_MESSAGE1"),
            props.getStringProperty("icq.STATUS_MESSAGE2"), e.getTime(), e.getMsgID(), e.getSenderID(), e.getSenderTcpVersion());
        }catch(ConvertStringException ex) {
        }
    }

    /**
     * Обработка входа
     */
    @Override
    public void onLogin() {
        connected = true;
        OscarInterface.changeStatus(con, new StatusModeEnum(Integer.parseInt(props.getStringProperty("icq.status")), props.getBooleanProperty("ball.of.joy.on.off")));
        OscarInterface.changeXStatus(con, new XStatusModeEnum(Integer.parseInt(props.getStringProperty("icq.xstatus"))), new ClientModeEnum(Integer.parseInt(props.getStringProperty("icq.client"))));
        FullUserInfo s = new FullUserInfo();
        s.setAuthNeed(true);
        s.setWebAware(true);
        Log.info("ICQ UIN " + screenName + " Online");
        con.getContactList();
        if(!props.getBooleanProperty("chat.IgnoreOfflineMsg")) OscarInterface.requestOfflineMessages(con);
    }

    /**
     * Обработка выхода
     */
    @Override
    public void onLogout(Exception e) {
        StaticData.setOnline(screenName, true);
        StaticData.setFirstReqRooster(screenName, false);
        Log.error("Разрыв соединения: " + screenName + " - " + server + ":" + port
        + " По причине: " + e.getMessage().toString());
        try {
            Log.info("Переподключение через 1 минуту...");
            Thread.sleep(60000);
            this.reConnect();
        } catch (Exception ex) {
            connected = false;
        }
    }

    /**
     * Список X-Статусов (Пригодится для смены)
     */
    public void ListStatus(ICQProtocol proc, String uin){
        String s = "Список XStatus'ов\n" ;
        s+="1 - Сердитый\n";
        s+="2 - Купаюсь\n";
        s+="3 - Уставший\n";
        s+="4 - Вечеринка\n";
        s+="5 - Пью пиво\n";
        s+="6 - Думаю\n";
        s+="7 - Кушаю\n";
        s+="8 - Телевизор\n";
        s+="9 - Друзья\n";
        s+="10 - Пью чай/кофе\n";
        s+="11 - Слушаю музыку\n";
        s+="12 - Дела\n";
        s+="13 - В кино\n";
        s+="14 - Развлекаюсь\n";
        s+="15 - Телефон\n";
        s+="16 - Играю \n";
        s+="17 - Учёба \n";
        s+="18 - Магазин \n";
        s+="19 - Болею \n";
        s+="20 - Сплю \n";
        s+="21 - Отрываюсь \n";
        s+="22 - В интернете \n";
        s+="23 - На работе \n";
        s+="24 - Печатаю \n";
        s+="25 - Пикник \n";
        s+="26 - КПК \n";
        s+="27 - Мобильник \n";
        s+="28 - Засыпаю \n";
        s+="29 - Туалет \n";
        s+="30 - Вопрос \n";
        s+="31 - Дорога \n";
        s+="32 - Сердце \n";
        s+="33 - Поиск \n";
        s+="34 - Дневник\n";
        s+="35 - Секс\n";
        s+="36 - RuLove\n";
        s+="37 - Курю\n";
        proc.mq.add(uin, s);
    }

    /**
     * Установка флага статуса
     * @param i номер status
     */
    public void setFlafStatus(int i) {
        int sf = StatusFlagEnum.DCAUTH;
        switch (i)  {
            case 0 : sf = StatusFlagEnum.WEBAWARE; break;
            case 1 : sf = StatusFlagEnum.SHOWIP; break;
            case 2 : sf = StatusFlagEnum.BIRTHDAY; break;
            case 3 : sf = StatusFlagEnum.WEBFRONT; break;
            case 4 : sf = StatusFlagEnum.DCDISABLED; break;
            case 5 : sf = StatusFlagEnum.DCAUTH; break;
            case 6 : sf = StatusFlagEnum.DCCONT; break;
            case 7 : sf = StatusFlagEnum.DCALLOWED; break;
            case 8 : sf = StatusFlagEnum.NONE; break;
        }
        OscarInterface.changeFlagStatus(con, new StatusFlagEnum(sf));
    }

    /**
     * Установка X-статуса
     * @param n - номер х-статуса
     * @param text - текст х-статуса
     */
    public void setXStatus( int n, String text ){
        if ( n >= 0 && n <= 37 ){
            xStatusId = n;
            props.setStringProperty( "icq.xstatus",Integer.toString(xStatusId));
        }
        if ( !text.equals( "" ) ){
            xStatusText = text;
            props.setStringProperty("icq.STATUS_MESSAGE2", xStatusText);
        }
        OscarInterface.changeXStatus(con, new XStatusModeEnum(xStatusId), new ClientModeEnum(Integer.parseInt(props.getStringProperty("icq.client"))));
    }

    /**
     * Для авто-смены статуса
     * @param number
     */
    public void setXStatusNumber(int number){
        OscarInterface.changeXStatus(con, new XStatusModeEnum(number), new ClientModeEnum(Integer.parseInt(props.getStringProperty("icq.client"))));
    }

    @Override
    public void onMessageMissed(MessageMissedEvent e) {
        Log.debug("Message from " + e.getUin() + " can't be recieved because " + e.getReason()  +
        " count="+e.getMissedMsgCount());
    }

    @Override
    public void onMessageError(MessageErrorEvent e) {
        Log.error("Message error " + e.getError().toString());
    }
    
    @Override
    public void onAffilationsUserInfo(MetaAffilationsUserInfoEvent e) {
        if(metas != null) metas.addMetaResult(e);
        if(metas != null) sendMsg(metas.getOwnerUIN(), metas.getTextResult());
    }
    
    @Override
    public void onNotesUserInfo(MetaNoteUserInfoEvent e) {
        if (metas != null) metas.addMetaResult(e);
    }
    
    @Override
    public void onMoreUserInfo(MetaMoreUserInfoEvent e) {
        if (metas != null) metas.addMetaResult(e);
    }
    
    @Override
    public void onInterestsUserInfo(MetaInterestsUserInfoEvent e) {
        if (metas != null) metas.addMetaResult(e);
    }
    
    @Override
    public void onWorkUserInfo(MetaWorkUserInfoEvent e) {
        if (metas != null) metas.addMetaResult(e);
    }
    
    @Override
    public void onBasicUserInfo(MetaBasicUserInfoEvent e) {
        if (metas != null) metas.addMetaResult(e);
    }
 
    /**
     * Изменение приватного статуса
     */
    public void privateStatus() {
        try {
            int privatestatus=Integer.parseInt(props.getStringProperty("icq.pstatus"));    
            PrivateStatus ps = PrivateStatus.UNVIS_ALL;
            switch (privatestatus) {
                case 1:
                    ps = PrivateStatus.VIS_ALL;
                    break;
                case 2:
                    ps = PrivateStatus.UNVIS_ALL;
                    break;
                case 3:
                    ps = PrivateStatus.VIS_LIST;
                    break;
                case 4:
                    ps = PrivateStatus.UNVIS_LIST;
                    break;
                case 5:
                    ps = PrivateStatus.CONTACT_LIST;
                    break;
            }
            OscarInterface.changePrivateStatus(con, ps);
        } catch (Exception e2) {
            Log.error(e2.getMessage().toString());
        }
    }

   /**
    * Запрос авторизации
    * @param uin
    * @param msg 
    */
    public void authRequest(String uin, String msg){}

    public void requestUserInfo(String oUIN, String tUIN) {
        metas = new MetaAckResults(oUIN, tUIN);
        OscarInterface.requestFullUserInfo(con, tUIN);
    }

    /**
     * Добавляем контакта 
     * @param contact - UIN пользователя
     * @param group - название группы
     */
    public void addContactList(String contact, String group) {
        if(!props.isRoster()) return;
        try {
            Thread.sleep(4000);
            if(GroupByName(group)) this.addGroup(group);
            if(ContactByUIN(contact)) con.getContactList().addContact(contact, group);
        } catch (Exception ex) {
            Log.error("Error " + ex.getMessage().toString());
        }
    }
 
    /**
     * Проверим создана группа или нет
     * @param group
     * @return 
     */
    public boolean GroupByName(String group) {
        if(con.getContactList().getGroupByName(group)==null) return true; // Разрешим создать группу
        Log.info("Группа "+group+" уже добавлена");
        return false;// запретим создать
    }

    /**
     * Проверим добавлен уин или нет
     * @param group
     * @return 
     */
    public boolean ContactByUIN(String uin) {
        if(con.getContactList().getContactById(uin)==null) return true; // Разрешим добавить контакт
        Log.info("Уин "+uin+" уже добавлен");
        return false;//запретим добавить
    }

    /**
     * Удаление пользователя с UIN
     * @param uin - UIN пользователя
     */
    public void RemoveContactList(String uin) {
        if(!props.isRoster()) return;
        try {
            con.getContactList().removeContact(uin);
        } catch (ContactListOperationException ex) {
            Log.error("Error " + ex.getMessage().toString());
        }
    }

    /**
     * Добавляем новую группу
     * @param group 
     */
    public void addGroup(String group){
        try {
            con.getContactList().addGroup(group);
        } catch (ContactListOperationException ex) {
            Log.error("Error " + ex.getMessage().toString());
        }
    }

    /**
     * Удаление группы по ее названию
     * @param group 
     */
    public void delGroup(String group){
        try {
            con.getContactList().delGroup(group);
        } catch (ContactListOperationException ex) {
            Log.error("Error " + ex.getMessage().toString());
        }
    }

   /**
    * Удалить себя из контакт листа пользователя с UIN
    * @param uin у кого удаляемя
    */ 
    public void removeYourself (String uin){
        con.getContactList().removeYourself(uin);
    }

    /**
     * Отправляем пользователю с UIN сообщение "You were added" ("Ваc добавили")
     * @param uin получатель сообщения о добавлении
     */ 
    public void sendYouWereAdded (String uin){
        con.getContactList().sendYouWereAdded(uin);
    }

    /**
     * Запрашиваем КЛ
     */ 
    public void sendContatListRequest(){
        OscarInterface.sendContatListRequest(con);
    }

   /**
    * Находим пользователя по UIN
    * @param uin 
    */
    public void findUsersByUIN(String uin){
        OscarInterface.findUsersByUIN(con, uin);
    }

   /**
    * Оффлайн сообщение
    * @param e 
    */
    public void onOfflineMessage(OfflineMessageEvent e) {
        Log.talk("Оффлайн сообщение: "+e.getMessage().toString()+" от UIN:"+e.getSenderUin());
    }

    public void onStatusResponse(StatusEvent se) {}
    public void onIncomingUrl(IncomingUrlEvent iue) {}
    public void onMessageAck(MessageAckEvent mae) {}
    public void onXStatusResponse(XStatusResponseEvent e) {}
    public void onShortUserInfo(MetaShortUserInfoEvent e) {}
    public void IncomingUserEvent(IncomingUserEvent e) {}
    public void onEmailUserInfo(MetaEmailUserInfoEvent e) {}
    public void setStatus(int status) {
        setXStatusNumber(status);
    }
    public void getStatus(String sn, int status) {}
    public void addContactList(String sn) {}

    public boolean userInfoRequest(String sn, String rsn) {
        lastInfo = sn;
        return true;
    }
}
